﻿/* Copyright 2011 Teun Duynstee
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using System.Configuration;
using MongoDB.Driver;
using MongoDB.Bson;
using System.Globalization;
using System.Collections;

namespace MongoAspnetProviders.Unittests
{
    [TestFixture]
    public class ProfileProviderTests
    {
        [Test]
        public void Absent_Username_Should_Throw()
        {
            ProfileProvider prov = new ProfileProvider(new TestStore());
            try
            {
                prov.GetPropertyValues(new SettingsContext(), new SettingsPropertyCollection());
            }
            catch (Exception)
            {
                return;
            }
            Assert.Fail("Exception waasn't thrown");
        }
        [Test]
        public void Serialization_of_Datetime_Should_Return_Same()
        {
            TestStore tst = new TestStore();
            ProfileProvider prov = new ProfileProvider(tst);

            prov.SetPropertyValues(GetSettingsContext(), 
                GetSettingsPropertyValues(
                DateTime.Today,
                DateTime.Now 
                ));
            Assert.That(tst.TheProfile.Properties[0].IsDateTime);
            Assert.That(tst.TheProfile.Properties[0].AsDateTime.ToLocalTime() == DateTime.Today);
            Assert.That(tst.TheProfile.Properties[1].IsDateTime);
            Assert.That(DateTime.Now.ToUniversalTime() - tst.TheProfile.Properties[1].AsDateTime  < TimeSpan.FromSeconds(1));
            Console.WriteLine("Time between Now before and Now after: {0}", DateTime.Now.ToUniversalTime() - tst.TheProfile.Properties[1].AsDateTime);

        }
        [Test]
        public void Serialization_of_Double_Should_Return_Same()
        {
            TestStore tst = new TestStore();
            ProfileProvider prov = new ProfileProvider(tst);

            prov.SetPropertyValues(GetSettingsContext(),
                GetSettingsPropertyValues(
                2d
                ));
            Assert.That(tst.TheProfile.Properties[0].IsDouble);
            Assert.That(tst.TheProfile.Properties[0].AsDouble == 2d);

        }
        [Test]
        public void Serialization_And_Deserialization_Numerics_Should_Return_Same()
        {
            TestStore tst = new TestStore();
            ProfileProvider prov = new ProfileProvider(tst);

            SettingsPropertyValueCollection valuesIn = GetSettingsPropertyValues(
                2d,
                1,
                long.MinValue,
                2f, 
                2.001m,
                (byte)2,
                (short)2,
                System.UInt64.MaxValue /2 /*System.UInt64.MaxValue cannot be stored in BSON without overflowing*/
                );
            prov.SetPropertyValues(GetSettingsContext(),valuesIn); 

            Console.WriteLine(tst.TheProfile.Properties.ToJson());

            SettingsPropertyValueCollection valuesOut = prov.GetPropertyValues(GetSettingsContext(), 
                GetSettingProperties(
                2d,
                1,
                long.MinValue,
                2f,
                2m,
                (byte)2,
                (short)2,
                ulong.MinValue
                    )
                );

            CheckEqualValues(valuesIn, valuesOut);

        }
        [Test]
        public void Serialization_And_Deserialization_NullableTypes_Should_Return_Same()
        {
            TestStore tst = new TestStore();
            ProfileProvider prov = new ProfileProvider(tst);

            SettingsPropertyValueCollection valuesIn = GetSettingsPropertyValues(
                new object[]{
                (int?)1,
                (int?)null,
                (long?)123,
                (long?)null,
                (ConsoleColor?)ConsoleColor.Red
                },
                new Type[]{
                    typeof(int?),
                    typeof(int?),
                    typeof(long?),
                    typeof(long?),
                    typeof(ConsoleColor?)
                }
                );
            prov.SetPropertyValues(GetSettingsContext(), valuesIn);

            Console.WriteLine(tst.TheProfile.Properties.ToJson());

            SettingsPropertyValueCollection valuesOut = prov.GetPropertyValues(GetSettingsContext(),
                GetSettingProperties(
                (int?)0,
                (int?)null,
                (long?)0,
                (long?)null,
                (ConsoleColor?)ConsoleColor.Red
                    )
                );

            CheckEqualValues(valuesIn, valuesOut);

        }
        [Test]
        public void Serialization_And_Deserialization_Strings_Should_Return_Same()
        {
            TestStore tst = new TestStore();
            ProfileProvider prov = new ProfileProvider(tst);

            SettingsPropertyValueCollection valuesIn = GetSettingsPropertyValues(
                String.Empty,
                "dfvs v dfkvsdkfvwsdfvsdfvs dfvn sdkfvn wdkfvnwdkfvnskfvndfvndv ndv jvnskn .",
                'w',
                "Bladieëíù-€°",
                '\x1234'
                );
            prov.SetPropertyValues(GetSettingsContext(), valuesIn);

            Console.WriteLine(tst.TheProfile.Properties.ToJson());

            SettingsPropertyValueCollection valuesOut = prov.GetPropertyValues(GetSettingsContext(),
                GetSettingProperties(
                "",
                "",
                'a',
                "",
                'a'
                    )
                );

            CheckEqualValues(valuesIn, valuesOut);

        }
        [Test]
        public void Serialization_And_Deserialization_Date_Should_Return_Same()
        {
            TestStore tst = new TestStore();
            ProfileProvider prov = new ProfileProvider(tst);

            SettingsPropertyValueCollection valuesIn = GetSettingsPropertyValues(
                DateTime.Today.ToUniversalTime(),
                TimeSpan.FromSeconds(20)
                );
            prov.SetPropertyValues(GetSettingsContext(), valuesIn);

            SettingsPropertyValueCollection valuesOut = prov.GetPropertyValues(GetSettingsContext(),
                GetSettingProperties(
                    DateTime.Today.ToUniversalTime(),
                    TimeSpan.FromSeconds(20)
                    )
                );

            CheckEqualValues(valuesIn, valuesOut);

        }
        [Test]
        public void Serialization_And_Deserialization_Collections_Should_Return_Same()
        {
            TestStore tst = new TestStore();
            ProfileProvider prov = new ProfileProvider(tst);

            Stack<string> stack = new Stack<string>();
            stack.Push("one");
            stack.Push("two");
            stack.Push("three");
            
            SettingsPropertyValueCollection valuesIn = GetSettingsPropertyValues(
                new int[] { 1, 2, 3, 4 },
                new List<byte>() { 23, 76, 255 },
                stack,
                new Dictionary<string, int>()
                {
                    {"one", 1},
                    {"two", 2},
                    {"four", 4}
                },
                new Dictionary<object, object>()
                {
                    {"one", 1},
                    {"two", 2},
                    {4, "four"}
                }
                );
            prov.SetPropertyValues(GetSettingsContext(), valuesIn);

            Console.WriteLine(tst.TheProfile.Properties.ToJson());

            SettingsPropertyValueCollection valuesOut = prov.GetPropertyValues(GetSettingsContext(),
                GetSettingProperties(
                new int[0],
                new List<byte>(),
                stack,
                new Dictionary<string, int>(),
                new Dictionary<object, object>()
                    )
                );

            CheckEqualValues(valuesIn, valuesOut);

        }
        [Test]
        public void Serialization_And_Deserialization_CustomClass_Should_Return_Same()
        {
            TestStore tst = new TestStore();
            ProfileProvider prov = new ProfileProvider(tst);

            SettingsPropertyValueCollection valuesIn = GetSettingsPropertyValues(
                new A() { Num = 12, Str = "sadf" },
                new A() { Num = 7, Str="zvc"}
                );
            prov.SetPropertyValues(GetSettingsContext(), valuesIn);

            Console.WriteLine(tst.TheProfile.Properties.ToJson());

            SettingsPropertyValueCollection valuesOut = prov.GetPropertyValues(GetSettingsContext(),
                GetSettingProperties(
                new A(),
                new A()
                    )
                );

            CheckEqualValues(valuesIn, valuesOut);

        }
        private class A
        {
            public int Num { get; set; }
            public string Str { get; set; }
            public override bool Equals(object obj)
            {
                A other = obj as A;
                return Num.Equals(other.Num) && Str.Equals(other.Str);
            }
            public override int GetHashCode()
            {
                return Num.GetHashCode() + Str.GetHashCode();
            }
        }
        [Test]
        public void Serialization_And_Deserialization_OtherBaseTypes_Should_Return_Same()
        {
            TestStore tst = new TestStore();
            ProfileProvider prov = new ProfileProvider(tst);

            SettingsPropertyValueCollection valuesIn = GetSettingsPropertyValues(
                Guid.NewGuid(),
                new Version(1,2,4),
                true,
                PlatformID.Unix,
                Bla.SecondVal,
                CultureInfo.GetCultureInfo("nl-BE"),
                CultureInfo.CurrentCulture,
                DateTimeOffset.Now,
                new BitArray(new bool[]{true, false, true, true, true, false, false}),
                new Uri("ftp://127.0.0.1:45/fefweefffff/f%wef.lll")
                );
            prov.SetPropertyValues(GetSettingsContext(), valuesIn);
            Console.WriteLine(tst.TheProfile.Properties.ToJson());
            SettingsPropertyValueCollection valuesOut = prov.GetPropertyValues(GetSettingsContext(),
                GetSettingProperties(
                Guid.Empty,
                new Version(),
                false,
                PlatformID.Xbox,
                Bla.FirstVal,
                CultureInfo.CurrentCulture,
                CultureInfo.CurrentCulture,
                DateTimeOffset.Now,
                new BitArray(0),
                new Uri("http://www.funda.nl")
                    )
                );

            CheckEqualValues(valuesIn, valuesOut);

        }
        public enum Bla: byte {FirstVal=1, SecondVal=2 }
        private static void CheckEqualValues(SettingsPropertyValueCollection valuesIn, SettingsPropertyValueCollection valuesOut)
        {
            for (int i = 0; i < valuesIn.Count; i++)
            {
                SettingsPropertyValue propIn = (SettingsPropertyValue)valuesIn["Property " + i];
                SettingsPropertyValue propOut = (SettingsPropertyValue)valuesOut["Property " + i];
                Assert.AreEqual(propIn.PropertyValue, propOut.PropertyValue);
            }
        }

        private SettingsPropertyCollection GetSettingProperties(params object[] defaultValues)
        {
            SettingsPropertyCollection collection = new SettingsPropertyCollection();
            for (int i = 0; i < defaultValues.Length; i++)
            {
                Type t = defaultValues[i] == null ? typeof(object) : defaultValues[i].GetType();
                SettingsProperty prop = new SettingsProperty(String.Format("Property {0}", i), t, null, false,
                        defaultValues[i], SettingsSerializeAs.String, null, false, false);
                collection.Add(prop);
            }
            return collection;

        }

        private SettingsPropertyValueCollection GetSettingsPropertyValues(params object[] values)
        {
            List<Type> types = new List<Type>();
            for (int i = 0; i < values.Length; i++)
            {
                Type t = values[i] == null ? typeof(object) : values[i].GetType();
                types.Add(t);
            }
            return GetSettingsPropertyValues(values, types.ToArray());
        }
        private SettingsPropertyValueCollection GetSettingsPropertyValues(object[] values, Type[] types)
        {
            SettingsPropertyValueCollection collection = new SettingsPropertyValueCollection();
            for (int i = 0; i < values.Length; i++)
            {
                SettingsProperty prop = new SettingsProperty(String.Format("Property {0}", i), types[i], null, false,
                        null, SettingsSerializeAs.String, null, false, false);
                SettingsPropertyValue propval = new SettingsPropertyValue(prop);
                propval.PropertyValue = values[i];
                collection.Add(propval);
            }
            return collection;
        }

        private SettingsContext GetSettingsContext()
        {
            SettingsContext context = new SettingsContext();
            context.Add("UserName", "TestUser");
            context.Add("IsAnonymous", false);
            return context;
        }
    }
    public class TestStore : IStoreWrapper<Profile>
    {
        public Profile TheProfile { get; set; }
        #region IStoreWrapper<Profile> Members

        public void Update(IMongoQuery where, IMongoUpdate update, UpdateFlags flags)
        {
            TheProfile = new Profile();
            TheProfile.Properties = (update as BsonDocument)["Properties"].AsBsonDocument;
        }

        public Profile FindOne(IMongoQuery searchMe)
        {
            return this.TheProfile;
        }


        public IList<Profile> Find(IMongoQuery where)
        {
            throw new NotImplementedException();
        }

        public IList<Profile> Find(IMongoQuery where, int from, int limit, out int total)
        {
            throw new NotImplementedException();
        }


        public SafeModeResult Remove(IMongoQuery where)
        {
            throw new NotImplementedException();
        }

        public void Save(Profile obj)
        {

        }

        #endregion
    }
}
